# tomboyweb.py
# 
# This code is based off the 'tasks' example at:
#
#  http://code.google.com/p/google-app-engine-samples/
#
# Copyright 2009 Douglas V Johnston
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



#  Some global thoughts:
#   - Layout should allow notes to be viewed "in page", without changing the 
#       surrounding UI, instead of changing the page, as is done now
#   - Should add message pinning, to mirro those used in Tomboy
#   - Tags should be obvious. More so than in Tomboy 
#   - Note sharing is so easy, it should be done. This makes sync and downloading
#       more challenging, how should this be done?
#   - Think about if Tasque should play a role in this
#   - Integrate with Google Calendar?
#   - see: momolog.com for another GAE implementation of online notes





__author__ = "Douglas V. Johnston"

import datetime
import os
import random
import string
import sys
import wsgiref.handlers

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import login_required
from google.appengine.ext.webapp.util import run_wsgi_app


#Turn on debug
_DEBUG = True


# Add our custom Django template filters to the built in filters
template.register_template_library('templatefilters')


# Debug routines to simulate user auth
#def login(self, user = "doug@xmail.com"):
#    users.get_current_user = lambda user=user : users.User(user) if user else None
#def logout(self):
#    self.login(None)


class BaseRequestHandler(webapp.RequestHandler):
  """Supplies a common template generation function.

  When you call generate(), we augment the template variables supplied with
  the current user in the 'user' variable and the current webapp request
  in the 'request' variable.
  """
  def generate(self, template_name, template_values={}):
    values = {
      'request': self.request,
      'user': users.GetCurrentUser(),
      'login_url': users.CreateLoginURL(self.request.uri),
      'logout_url': users.CreateLogoutURL('http://' + self.request.host + '/'),
      'debug': self.request.get('deb'),
      'application_name': 'Tomboy Online',
    }
    values.update(template_values)
    directory = os.path.dirname(__file__)
    path = os.path.join(directory, os.path.join('templates', template_name))
    self.response.out.write(template.render(path, values, debug=_DEBUG))


    #TODO: Add a real 403/404 handler
    #HELP: Make this work



class TomboyNote(db.Model):
    #TODO: Flush this out a bit
    #HELP: Ideas here would be appreciated
    note    = db.TextProperty()
    noteid  = db.StringProperty()
    owner   = db.UserProperty()
    title   = db.StringProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    sticky  = db.BooleanProperty(default=False)
    shared  = db.BooleanProperty(default=False)
    version = db.IntegerProperty(required=True, default = 0)
    content = db.TextProperty()

    @staticmethod
    def getCurrentUserNotes():
      return TomboyNote.getUserNotes(users.GetCurrentUser())

    @staticmethod
    def getUserNotes(user):
      if not user: return []
      memberships = db.Query(TomboyNote).filter('owner =', user)
      #memberships = db.Query(TomboyNote)
      return [m for m in memberships]

    def current_user_has_access(self):
      """Returns true if the current user has access to this note."""
      return self.user_has_access(users.GetCurrentUser())

    def user_has_access(self, user):
      """Returns true if the given user has access to this note."""
      if not user: return False
      if user == self.owner:
        return True
      else:
        return False



#HELP: Is this the best way to handle shared notes?
class NoteShares(db.Model):
  """Represents the many-to-many relationship between shared notes and Users.
  This is essentially the Note ACL.
  """
  note = db.Reference(TomboyNote, required=True)
  user = db.UserProperty(required=True)


#TODO: Add error handling, make better response
class SendNote(webapp.RequestHandler):
    def get(self):
	self.response.out.write("<html><title>Send Note</title><body>Not supported</body></html>")
	
    def post(self):
      #login('doug@mail.com')
      if not (users.get_current_user()):
        self.response.out.write("NOT AUTH")
        return

      note = TomboyNote(title=self.request.get('noteid'))
      note.note = self.request.get('note')
      note.noteid = self.request.get('noteid')
      note.owner = users.get_current_user()
      note.put()
      self.response.out.write("<html><title>Send Note</title><body>Note Sent</body></html>")
	

#TODO: Make it not-so-easy to GLOBALY remove all TomboyNote's :)
class DeleteAllNotes(webapp.RequestHandler):
    def get(self):
	notes = db.GqlQuery("SELECT * FROM TomboyNote")
	for note in notes:
	    note.delete()


class ListNotes(BaseRequestHandler):
  """Shows the list of the users notes"""
  @login_required
  def get(self):
    #TODO: Filter this so the user can view pages of [10,20,50] notes per page
    notes = TomboyNote.getCurrentUserNotes()
    self.generate('index.html',{'notes':notes})



#TODO: This is a little funky right now, either function it out, or rethink having a global handler
class NoteListAction(BaseRequestHandler):
  def post(self):
    action = self.request.get('action')
    note_key = self.request.get('note', allow_multiple=False)

    if not action in ['Download', 'Delete', 'Search']:
      self.error(403)
      return

    if action == 'Download':
      note = TomboyNote.get(note_key)
      if not note or not note.current_user_has_access():
        self.error(403)
        return
    
      self.response.headers['Content-Type'] = "text/xml"
      self.response.out.write(str(note.note))
      
    if action == 'Search':
      n = TomboyNote.all()
      n.filter("title =",self.request.get('searchbox'))
      n.filter('owner =', users.get_current_user())
      notes = n.fetch(20)
     
      self.generate('index.html',{'notes':notes,'listtype':'search'})



class ShowNote(BaseRequestHandler):
  """Display a notes contents in a variety of formats"""
  # The different task list output types we support: content types and
  # template file extensions
  _OUTPUT_TYPES = {
    'default': ['text/html', 'html'],
    'html': ['text/html', 'html'],
    'xml' : ['text/xml', 'xml'],
    'atom': ['application/atom+xml', 'xml'],
  }

  def get(self):
    note = TomboyNote.get(self.request.get('id'))
    if not note:
      print "Can't find note"
      self.error(403)
      return

    # Choose a template based on the output type
    output_name = self.request.get('output')
    output_name_list = ShowNote._OUTPUT_TYPES.keys()
    if output_name not in output_name_list:
      output_name = output_name_list[0]
    output_type = ShowNote._OUTPUT_TYPES[output_name]
    
    # Validate this user has access to this note.
    if not note.current_user_has_access():
	#TODO: additional code goes here for public/shared notes
        user = users.GetCurrentUser()
        if not user:
          self.redirect(users.CreateLoginURL(self.request.uri))
        else:
          print user 
          print note.owner 
          self.error(403)
        return

    #TODO: Additional filtering
    #TODO: Ordering 



    #reformat the note a bit
    
    #TODO: Move formatting to seperate function
    #TODO: toally redo formatting - this needs to be 
    #      a nice tomboy-xml -> html formatter
    #HELP: need ideas here
    found = True
    pos = str(note.note).find("<note-content",0)
    ppos = pos
    endOfNotePos = str(note.note).find("</note-content",0)
    tmp_str = ""
    while found:
      found = False
      pos = str(note.note).find("\n",ppos)
      if pos > 0:
        found = True
        tmp_str += str(note.note)[ppos:pos] + "<br>"
        ppos = pos+1
      if pos > endOfNotePos:
        found = False
      
    tmp_str += str(note.note)[ppos:endOfNotePos]
    note.content = tmp_str
      

    self.response.headers['Content-Type'] = output_type[0]
    #TODO: Add templetes for the other format types
    self.generate('shownote_' + output_name + '.' + output_type[1], {
      'note': note
    })

class CreateNewNoteAction(BaseRequestHandler):
  def post(self):
    user = users.GetCurrentUser()
    title = self.request.get('title')
    content = self.request.get('content')
    if not user or not title:
      self.error(403)
      return

    #TODO: This should wrap the user content in a valid Tomboy note
    #TODO: Put wrapping in own class
    tmp_note = "<note-content>" + content + "</note-content>"
    note = TomboyNote(owner=user, title=title, note=tmp_note)
    note.put()
    if self.request.get('next'):
      self.redirect(self.request.get('next'))
    else:
      #self.redirect('/list?id=' + str(note.key()))
      self.redirect('/')


#TODO: Make the links more uniform
application = webapp.WSGIApplication(
    [('/', ListNotes),
     ('/createnewnote.do', CreateNewNoteAction),
     ('/inboxaction.do', NoteListAction),
     ('/listnotes', ListNotes),
     ('/shownote', ShowNote),
     #the following is useful but dangerous, only turn on when needed
     #('/deletenote',DeleteAllNotes),
     ('/sendnote',SendNote)],
    debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()
